package javaAdvanced;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * 泛型的测试的学习类
 * <p/>
 * 泛型：存在于编译器期
 *
 * @author cuiH
 * @version 1.0.1
 */
public class GenericTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Collection<String> collection1 = new ArrayList<String>();             //面向借口编程或者面向父类编程规范
        Collection<Integer> collection2 = new ArrayList<Integer>();

        System.out.println("ArrayList<String>和ArrayList<Integer>的反射是否相同：" + (collection2.getClass() == collection1.getClass()));

//        collection2.add("string");//编译器会报错
        collection2.getClass().getMethod("add", Object.class).invoke(collection2, "string");  //反射调用方法，运行期插入内容
        System.out.println("ArrayList<Integer>()包括：" + collection2);                       //result:[string]
        /**
         * 分析：泛型本质是给编译器看的，仅存于编译期，运行期则没有
         * 例如第一个输出：两个的字节码是完全相同的
         * 第二个例子在<Integer>中在运行期添加了string类型
         */


        /**
         * 泛型不存在继承问题，
         * 例如String 和Object 不存在泛型的继承（下面两行代码都会报错）
         */
//        Vector<String> v1 = new Vector<Object>();
//        Vector<Object> v2 = new Vector<String>();


        printCollection(new ArrayList<BigDecimal>());   //？类型通配符
        printCollection(new ArrayList<Integer>());

        /**
         * 利用泛型，实现一个map进行迭代输出的例子
         * 因为：map是一个对象，无法进行迭代
         * 通过entrySet
         * 引用联想：有时候后台获取的参数，很多都是map（eg：spring jdbc）
         * 可以通过entrySet转变为set类型，然后前台可以直接调用getKey，和getValue进行jsp的输出
         */
        HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
        hashMap.put("崔小涣", 18);
        hashMap.put("崔大涣", 28);
        hashMap.put("崔老涣", 38);

        Set<Map.Entry<String, Integer>> entrySet = hashMap.entrySet();
        for (Map.Entry<String, Integer> entry : entrySet) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

        /**
         * 针对所有类型 交换数组中元素位置的方法测试
         * 注意泛型接受的都是对象类型，不能接受基本类型
         */
        String[] testStr = new String[]{"崔小涣", "崔大涣", "崔老涣"};
        Integer[] testInteger = new Integer[]{0, 1, 2};
        swap(testStr, 0, 1);
        swap(testInteger, 0, 1);
        System.out.println(Arrays.toString(testStr));   //result:[崔大涣, 崔小涣, 崔老涣]
        System.out.println(Arrays.toString(testInteger)); //result:[1, 0, 2]

        Object obj = "123";
        String int_2 = autoConvert(obj);
        System.out.println(int_2);

        /**
         * 泛型类
         * 限制了类内部的所有泛型条件
         * */
        GenericDao<ReflectPoint> dao = new GenericDao<ReflectPoint>();
        dao.add(new ReflectPoint(3,3));
        String str = dao.findById(1);

        /**
         * 可以得到指定容器的参数的类型。
         */
        Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
        System.out.println(applyMethod.getGenericParameterTypes()[0]);   //result：[java.util.Vector<java.util.Date>]

    }

    /**
     * 泛型中的？通配符
     * <p/>
     * 传输任意类型的泛型类型（主要用于的引用）
     * <p/>
     * 但是：进入之后不能调用和参数有关的方法
     * add()方法调用会报错，因为包含泛型类型信息；可以调用size,get等不牵涉类型的方法
     * <p/>
     * ？的扩展 extends X  引用扩展必须是由X扩展来的，X的子类，同时还可以添加“&”符号来进行定界
     */
    public static void printCollection(Collection<? extends Number> collection) {
        collection.size();                      //可以获取长度
        for (Object obj : collection) {             //可以进行输出
            System.out.println(obj);
        }

        collection = new HashSet<BigInteger>();   //可以引用其他通配符
    }

    private static <T> T add(T x, T y) {  //泛型的语法定义
        return null;
    }

    /**
     * 交换数组中两个元素的位置
     * 数组里面可能是各种类型
     */
    private static <T> void swap(T[] a, int i, int j) {
        T temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

    /**
     * 自动将Object类型转换为其他类型
     */
    private static <T> T autoConvert(Object obj) {
        return (T)obj;
    }

    /**
     * 将任意类型的数组中的所有元素,填充为某个对象
     * 应用泛型的类型识别
     */
    private static <T> void fillArray(T[] a, T obj) {
        for (T temp : a) {
            temp = obj;
        }
    }

    /**
     * 打印任意类型的集合，同时，添加额外元素
     * 泛型的方式，不同于通配符的方式
     */
    public static <T> void printAllType(Collection<T> collection, T... addObj) {
        Collections.addAll(collection, addObj);

        for (T obj : collection) {             //可以进行输出
            System.out.println(obj);
        }
    }


    /**
     * 获取容器内类型的具体泛型
     * 解决难点：泛型只存在于编译期，所以在运行期通过直接getClass没有直接获取类型的方法
     * 通过声明一个方法，可以获取其中参数的类型
     * @since 1.5
     * @param v
     */
    public static void applyVector(Vector<Date> v){
    }

}
/**
 * java 不能建立参数化类型的数组 eg:Collection<String>[] 这是不被支持的。
 * 泛型中可以添加多个类型参数，例如 map中
 *
 * 当方法中输入和输出类型，不确定的时候，往往需要用到泛型
 *
 */
